/*	Particle Simulator. Simulates the speed and position of particles and recalculates speed and position based on collisions. 
 *	Copyright (C) 2012 W.A. Garrett Weaver.
 *	This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 *	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 *	You should have received a copy of the GNU General Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *	simulate.c implements all the needed functions to simulate a particle system
 */

//include the header file for this program to implement the functions specified in it
#include "simulate.h"

/*	simulate calculates the positions, of all particles and barriers, at every point in time. 
 */
bool simulate(int timeSteps, float dt, int numberOfParticles, struct particle * particleArray, float ** particleXPositions, float ** particleYPositions, int numberOfBarriers, struct barrier * barrierArray)
{
	//allocate memory for the collision matrixs
	bool** particleParticleCollisions;
	bool** particleBarrierCollisions;
	particleParticleCollisions = (bool**)malloc(numberOfParticles * sizeof(bool*));
	particleBarrierCollisions = (bool**)malloc(numberOfParticles * sizeof(bool*));
	for(int i = 0; i < numberOfParticles; i++) 
	{
		particleParticleCollisions[i] = (bool*)malloc(numberOfParticles * sizeof(bool));
		particleBarrierCollisions[i] = (bool*)malloc(numberOfBarriers * sizeof(bool));
	}
	
	//fill in the initial positions of particles to the particleX and particleY position arrays
	for(int i = 0; i < numberOfParticles; i++)
	{
		particleXPositions[0][i] = particleArray[i].xPos;
		particleYPositions[0][i] = particleArray[i].yPos;
	}
	
	
	
	//iterate through every time step
	for (int t = 1; t < timeSteps; t++)
	{
		//fill the particleParticleCollisions matrix with zeros
		for(int i = 0; i < numberOfParticles; i++)
		{
			for(int j = 0; j < numberOfParticles; j++)
			{
				particleParticleCollisions[i][j] = 0;
			}
		}
		
		//fill in the collision matricies: particleParticleCollisions, particleBarrierCollisions
		generateCollisionMatrix(dt, numberOfParticles, particleArray, numberOfBarriers, barrierArray, particleParticleCollisions, particleBarrierCollisions);
		
		//for the particles that have collided, calculate their new positions
		//TODO: calculate new positions, based on collisions
		
		
	}
	
	
	
	//free dynamically allocated memory for collision matricies
	for(int i = 0; i < numberOfParticles; i++) 
	{
		free(particleParticleCollisions[i]);
		free(particleBarrierCollisions[i]);
	}
	free(particleParticleCollisions);
	free(particleBarrierCollisions);
	
	return true; 
}

/*	generateCollisionMatrix detects all the collisions and puts the collisions into a boolean matrix
 */
void generateCollisionMatrix(float dt, int numberOfParticles, struct particle * particleArray, int numberOfBarriers, struct barrier * barrierArray, bool ** particleParticleCollisions, bool ** particleBarrierCollisions)
{
	float particleCollisionPosition[2];
	
	/* Find collisions between particles
	 */
	//iterate through every particle p1
	for(int p1 = 0; p1 < numberOfParticles; p1++)
	{
		//iterate upto the ID value, of p1 with p2
		for(int p2 = 0; p2 < p1; p2++)
		{
			particleParticleCollisions[p1][p2] = findParticleParticleIntersectionPoint(dt, particleArray[p1], particleArray[p2], particleCollisionPosition);
		}
		
		//iterate through the barriers
		for(int b = 0; b < numberOfBarriers; b++)
		{
			//TODO: find the particle barrier intersection matrix
			
			
		}
	}
}

/* This function finds the intersection point, between the particles, and returns true if the particles intersect and puts the intersection point into the array intersectionPoint
 */
bool findParticleParticleIntersectionPoint(float dt, struct particle particleOne, struct particle particleTwo, float * intersectionPoint)
{
	//this will be the value that gets returned
	bool intersectionExists = false;
	
	//declare slopes of the particles
	float m1, m2;
	//declare intercepts for the particle paths
	float b1, b2;
	//declare particle 1 intersection time
	float ti;
	//declare particle 2 position points for the particles
	float x2i, y2i;
	float particleDistance; 
	
	//check to see if we have any verticle lines
	if(particleOne.xVel == 0 || particleTwo.xVel == 0)
	{
		//we know we have at-least one verticle line
		
		//if both the lines are verticle
		if(particleOne.xVel == 0 && particleTwo.xVel == 0)
		{
			//if both lines are within the the sum of the radius of the two particles, we may have a collision
			//TODO: find if the particles ever hit each other 
			
			
		}
		//if only the first particle is going verticle
		else if(particleOne.xVel == 0)
		{
			//TODO: calculate intersection in this case
			
			
		}
		//if only the second particle is going verticle
		else 
		{
			//TODO: calculate intersection in this case
			
			
		}
	}
	//if there are no verticle lines
	else 
	{
		//get m1 and b1, the slope and y intercept respectively
		m1 = particleOne.yVel / particleOne.xVel;
		b1 = particleOne.yPos - (m1 * particleOne.xPos);
		
		//get m2 and b2, the slope and y intercept respectively
		m2 = particleTwo.yVel / particleTwo.xVel;
		b2 = particleTwo.yPos - (m2 * particleTwo.xPos);
		
		//if the slopes are identical
		if(m1 == m2)
		{
			//if the slopes are identical, we either have an intersection everywhere, or nowhere
			
			//everywhere if the intercepts are within both radius's of the two particles
			//TODO: Find a way to detect if two parallel lines, on a cartesian grid, are within the sum of the radius of each other
			if(false)
			{
				//this case is complicated, we need to know if the two particles ever occupy the same point at the same time
				
				//we need to look at the parametric equations and see if there is a time when the particles share the same x and y coordinates
				//TODO: find if the particles ever hit each other 
				
				
			}
			//nowhere if the slopes are not the same
			else 
			{
				//do nothing, there is no intesection
			}

		}
		//if the slopes are not identical
		else 
		{
			//get the intersection point
			intersectionPoint[X_Comp] = (b2 - b1)/(m1 - m2);
			intersectionPoint[Y_Comp] = (m1 * intersectionPoint[X_Comp]) + b1;
			
			//find the particle 1 intersection time
			ti = ((intersectionPoint[X_Comp] - particleOne.xPos)/particleOne.xVel);
			
			//find where particle two is at at time ti
			x2i = (ti * particleTwo.xVel) + particleTwo.xPos;
			y2i = (ti * particleTwo.yVel) + particleTwo.yPos;
			
			//calculate the distance between the particles
			particleDistance = (float)sqrt(((intersectionPoint[X_Comp] - x2i) * (intersectionPoint[X_Comp] - x2i)) + ((intersectionPoint[Y_Comp] - y2i)*(intersectionPoint[Y_Comp] - y2i)));
			
			//test to see that we do have an intersection
			intersectionExists = (particleOne.radius + particleTwo.radius > particleDistance) && ti > 0 && ti <= dt;
		}

	}
	
	return intersectionExists;
}
